﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Xml;
using AutomationHelper;
using HPAGMRestAPIWrapper;
using System.Threading;
using System.Windows.Threading;
using System.Windows;
using System.IO;
using AGMLite.Windows.Notify;
using System.Windows.Controls.Primitives;
using Hardcodet.Wpf.TaskbarNotification;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using AGMLite.ViewModelClass;
using AGMLite.Windows;

namespace AGMLite.BaseClass
{
    public class AGMItem : INotifyPropertyChanged, IDataErrorInfo
    {
        public event PropertyChangedEventHandler PropertyChanged;


        #region Private members
        private static readonly object _syncObject = new object();

        private const double _defaultsyncinterval = 120;
        private string _name;
        private Common.AGMLiteEnum.EntityType _entityType;
        private int _syncinterval;
        private Dictionary<string, string> _errorcollection;
        private int _status;
        private int _total;
        private bool _isenabled;
        private string _cacheFile;
        private bool _canEditEntityType;

        private ObservableCollectionEx<AGMLiteEntityFilterField> _filterFieldCollection;
        private ObservableCollectionEx<AGMLiteEntityViewField> _viewFieldCollection; 

        private BackgroundWorker _syncWorker;
        #endregion

        #region Properties
        public TaskbarIcon TaskBarIcon { get; set; }
        public System.Timers.Timer ItemTimer { get; set; }
        public AGMProject Project { get; set; }
        public string GUID { get; set; }
        public string Message { get; set; }        
        public bool IsTimerRunning { get; set; }
        public bool IsEditing { get; set; }
        public bool IsRequireSync { get; set; }        
        public int LastStatus { get; set; }        
        public bool IsSyncImmediatelly { get; set; }

        public ObservableCollectionEx<AGMLiteEntity> LiteEntityCollection { get; set; }

        public bool CanEditEntityType
        {
            get
            {
                return _canEditEntityType;
            }
            set
            {
                if (value != _canEditEntityType)
                {
                    _canEditEntityType = value;
                    OnPropertyChanged("CanEditEntityType");
                }
            }
        }

        public int Total
        {
            get
            {
                return _total;
            }
            set
            {
                if (value != _total)
                {
                    _total = value;
                    OnPropertyChanged("Total");
                }
            }
        }       

        /// <summary>
        /// Item status, 0 - normal, 1 - warning, 2 - new item coming, 3 - syncing, 4 - paused
        /// </summary>
        public int Status
        {
            get
            {
                return _status;
            }
            set
            {
                if (value != _status)
                {
                    _status = value;
                    OnPropertyChanged("Status");
                }
            }
        }

        public bool IsEnabled
        {
            get
            {
                return _isenabled;
            }
            set
            {
                if (value != _isenabled)
                {
                    _isenabled = value;
                    OnPropertyChanged("IsEnabled");
                }
            }
        }

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                if (value != _name)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        public Common.AGMLiteEnum.EntityType EntityType
        {
            get
            {
                return _entityType;
            }
            set
            {
                if (value != _entityType)
                {
                    _entityType = value;
                    OnPropertyChanged("EntityType");
                }
            }
        }

        public int SyncInterval
        {
            get
            {
                return _syncinterval;
            }
            set
            {
                if (value != _syncinterval)
                {
                    _syncinterval = value;
                    OnPropertyChanged("SyncInterval");
                }
            }
        }

        public ObservableCollectionEx<AGMLiteEntityFilterField> FilterFieldCollection
        {
            get
            {
                return _filterFieldCollection;
            }
            set
            {
                if (value != _filterFieldCollection)
                {
                    _filterFieldCollection = value;
                    OnPropertyChanged("FilterFieldCollection");
                }
            }
        }

        public ObservableCollectionEx<AGMLiteEntityViewField> ViewFieldCollection
        {
            get
            {
                return _viewFieldCollection;
            }
            set
            {
                if (value != _viewFieldCollection)
                {
                    _viewFieldCollection = value;
                    OnPropertyChanged("ViewFieldCollection");
                }
            }
        }

        #endregion

        #region Error Info
        public string Error
        {
            get
            {
                if (_errorcollection.Count == 0)
                    return null;

                StringBuilder errorList = new StringBuilder();
                var errorMessages = _errorcollection.Values.GetEnumerator();
                while (errorMessages.MoveNext())
                    errorList.AppendLine(errorMessages.Current);

                return errorList.ToString();
            }
        }

        public string this[string propertyName]
        {
            get
            {
                string msg = null;
                switch (propertyName)
                {
                    case "Name":
                        if (string.IsNullOrEmpty(this.Name))
                            msg = "Item name cannot be empty";
                        break;
                    case "SyncInterval":
                        if (this.SyncInterval < _defaultsyncinterval)
                            msg = string.Format("Sync interval cannot less than {0} seconds", _defaultsyncinterval);
                        break;
                }

                if (msg != null && !_errorcollection.ContainsKey(propertyName))
                    _errorcollection.Add(propertyName, msg);
                if (msg == null && _errorcollection.ContainsKey(propertyName))
                    _errorcollection.Remove(propertyName);

                return msg;
            }
        }
        #endregion

        public AGMItem()
        {
            FilterFieldCollection = new ObservableCollectionEx<AGMLiteEntityFilterField>();
            ViewFieldCollection = new ObservableCollectionEx<AGMLiteEntityViewField>();
            _errorcollection = new Dictionary<string, string>();

            LiteEntityCollection = new ObservableCollectionEx<AGMLiteEntity>();

            InitialTimer();
            if (SyncInterval == 0) SyncInterval = 300;

            PropertyChanged += PropertyChangedHandler;
        }

        public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("IsEnabled"))
            {
                if ((sender as AGMItem).IsEnabled)
                {
                    StartTimer();
                }
                else
                {
                    StopTimer();
                }
            }
        }

        public AGMItem DeepCopy()
        {
            AGMItem clonedItem = (AGMItem)this.MemberwiseClone();
            clonedItem.FilterFieldCollection = new ObservableCollectionEx<AGMLiteEntityFilterField>(this.FilterFieldCollection);
            foreach (AGMLiteEntityFilterField ff in this.FilterFieldCollection)
            {
                clonedItem.FilterFieldCollection.Add(new AGMLiteEntityFilterField
                {
                    Operation = ff.Operation,
                    Field = ff.Field,                    
                    AvailableFieldOptions = ff.AvailableFieldOptions,
                    AvailableFields = ff.AvailableFields,
                    AvailableFieldTypes = ff.AvailableFieldTypes,
                    AvailableFieldsPool = ff.AvailableFieldsPool,
                });
            }
            clonedItem.ViewFieldCollection = new ObservableCollectionEx<AGMLiteEntityViewField>(this.ViewFieldCollection);
            foreach (AGMLiteEntityViewField vf in this.ViewFieldCollection)
            {
                clonedItem.ViewFieldCollection.Add(new AGMLiteEntityViewField
                {
                    Order = vf.Order,
                    Field = vf.Field,
                    AvailableFieldOptions = vf.AvailableFieldOptions,
                    AvailableFields = vf.AvailableFields,
                    AvailableFieldTypes = vf.AvailableFieldTypes,
                    AvailableFieldsPool = vf.AvailableFieldsPool,
                });
            }
            clonedItem.LiteEntityCollection = new ObservableCollectionEx<AGMLiteEntity>(this.LiteEntityCollection);
            foreach (AGMLiteEntity entity in this.LiteEntityCollection)
            {
                clonedItem.LiteEntityCollection.Add(new AGMLiteEntity
                {
                    Entity = entity.Entity,
                    IsNew = entity.IsNew,
                    Health = entity.Health,
                    HealthMessage = entity.HealthMessage,
                    Id = entity.Id,
                    BallonStaySeconds = entity.BallonStaySeconds,
                    Description = entity.Description,
                    EntityType = entity.EntityType,
                    Name = entity.Name,
                    Project = entity.Project,
                    TaskBarIcon = entity.TaskBarIcon,
                    ViewFields = entity.ViewFields,
                    EntityDetailControl = entity.EntityDetailControl,
                });
            }
            return clonedItem;
        }

        #region public methods
        public void Sync()
        {
            if (IsEnabled)
            {
                _cacheFile = System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "Cache", string.Format("{0}_{1}.txt", Project.GUID, GUID));
                _syncWorker = new BackgroundWorker();
                _syncWorker.DoWork += new DoWorkEventHandler(SyncWorker_DoWork);
                _syncWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(SyncWorker_RunWorkerCompleted);
                _syncWorker.RunWorkerAsync();
            }
        }

        #region Timer related
        public void StartTimer()
        {
            ItemTimer.Interval = SyncInterval == 0 ? TimeSpan.FromSeconds(_defaultsyncinterval).TotalMilliseconds : TimeSpan.FromSeconds(SyncInterval).TotalMilliseconds;            
            ItemTimer.Start();
            IsTimerRunning = true;
        }

        public void StopTimer()
        {
            ItemTimer.Stop();
            IsTimerRunning = false;
        }

        public void ResetTimer()
        {
            ItemTimer.Stop();
            IsTimerRunning = false;
            ItemTimer.Start();
            IsTimerRunning = true;
        }
        #endregion
        
        public void SaveLatestStatus()
        {
            lock (_syncObject)
            {
                try
                {
                    if (!string.IsNullOrEmpty(GUID))
                    {
                        string configFile = System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "settings.xml");
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.Load(configFile);
                        XmlNode itemNode = xmlDoc.SelectSingleNode(string.Format("Monitor/Projects/Project[@guid='{0}']/Items/Item[@guid='{1}']", Project.GUID, GUID));
                        if (itemNode.Attributes["laststatus"] == null)
                            XMLHelper.CreateAttribute(itemNode, "laststatus", Status.ToString());
                        else
                            itemNode.Attributes["laststatus"].Value = Status.ToString();

                        xmlDoc.Save(configFile);
                    }
                    
                }
                catch{
                }
            }
        }

        public void DeleteCacheFile()
        {
            if (System.IO.File.Exists(System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "Cache", string.Format("{0}_{1}.txt", Project.GUID, GUID))))
                System.IO.File.Delete(System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "Cache", string.Format("{0}_{1}.txt", Project.GUID, GUID)));
        }

        public void Save(bool isDelete)
        {
            XmlNode itemsNode;

            lock (_syncObject)
            {
                try
                {
                    string configFile = System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "settings.xml");
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(configFile);
                    XmlNode projectNode = xmlDoc.SelectSingleNode(string.Format("Monitor/Projects/Project[@guid='{0}']", Project.GUID));
                    itemsNode = projectNode.SelectSingleNode("Items");
                    if (itemsNode == null)
                        itemsNode = XMLHelper.CreateChildNode(projectNode, "Items");

                    XmlNode itemNode = itemsNode.SelectSingleNode(string.Format("Item[@guid='{0}']", GUID));
                    if (itemNode == null) // it is a new item, to add
                    {
                        XmlNode addItemNode = XMLHelper.CreateChildNode(itemsNode, "Item");
                        XMLHelper.CreateAttribute(addItemNode, "guid", GUID);
                        XMLHelper.CreateAttribute(addItemNode, "name", Name);
                        XMLHelper.CreateAttribute(addItemNode, "projectguid", Project.GUID);
                        XMLHelper.CreateAttribute(addItemNode, "entitytype", EntityType.ToString());
                        XMLHelper.CreateAttribute(addItemNode, "syncinterval", SyncInterval.ToString());
                        XMLHelper.CreateAttribute(addItemNode, "laststatus", LastStatus.ToString());
                        XMLHelper.CreateAttribute(addItemNode, "isenabled", IsEnabled.ToString());
                        XmlNode filtersNode = XMLHelper.CreateChildNode(addItemNode, "Filters");
                        foreach (AGMLiteEntityFilterField field in FilterFieldCollection)
                        {
                            XmlNode fieldNode = XMLHelper.CreateChildNode(filtersNode, "Field");
                            XMLHelper.CreateAttribute(fieldNode, "fieldtype",field.Field.Entity);
                            XMLHelper.CreateAttribute(fieldNode, "name", field.Field.Name);
                            XMLHelper.CreateAttribute(fieldNode, "value", field.Field.Value);
                            XMLHelper.CreateAttribute(fieldNode, "operation", field.Operation.ToString());
                        }                        
                        XmlNode viewsNode = XMLHelper.CreateChildNode(addItemNode, "Views");
                        foreach (AGMLiteEntityViewField field in ViewFieldCollection.OrderBy(o=>o.Order))
                        {
                            XmlNode fieldNode = XMLHelper.CreateChildNode(viewsNode, "Field");
                            XMLHelper.CreateAttribute(fieldNode, "fieldtype", field.Field.Entity);
                            XMLHelper.CreateAttribute(fieldNode, "name", field.Field.Name);
                            XMLHelper.CreateAttribute(fieldNode, "order", field.Order.ToString());
                        }
                    }
                    else
                    {
                        if (!isDelete) // edit some field's value
                        {
                            itemNode.Attributes["name"].Value = Name;
                            itemNode.Attributes["syncinterval"].Value = SyncInterval.ToString();
                            itemNode.Attributes["laststatus"].Value = LastStatus.ToString();
                            itemNode.Attributes["isenabled"].Value = IsEnabled.ToString();

                            XmlNode filtersNode = itemNode.SelectSingleNode("Filters");
                            if (filtersNode != null)
                                filtersNode.RemoveAll();
                            else
                                filtersNode = XMLHelper.CreateChildNode(itemNode, "Filters");
                            foreach (AGMLiteEntityFilterField field in FilterFieldCollection)
                            {
                                XmlNode fieldNode = XMLHelper.CreateChildNode(filtersNode, "Field");
                                XMLHelper.CreateAttribute(fieldNode, "fieldtype", field.Field.Entity);
                                XMLHelper.CreateAttribute(fieldNode, "name", field.Field.Name);
                                XMLHelper.CreateAttribute(fieldNode, "value", field.Field.Value);
                                XMLHelper.CreateAttribute(fieldNode, "operation", field.Operation.ToString());
                            }
                            XmlNode viewsNode = itemNode.SelectSingleNode("Views");
                            if (viewsNode != null)
                                viewsNode.RemoveAll();
                            else
                                viewsNode = XMLHelper.CreateChildNode(itemNode, "Views");
                            foreach (AGMLiteEntityViewField field in ViewFieldCollection.OrderBy(o=>o.Order))
                            {
                                XmlNode fieldNode = XMLHelper.CreateChildNode(viewsNode, "Field");
                                XMLHelper.CreateAttribute(fieldNode, "fieldtype", field.Field.Entity);
                                XMLHelper.CreateAttribute(fieldNode, "name", field.Field.Name);
                                XMLHelper.CreateAttribute(fieldNode, "order", field.Order.ToString());
                            }
                        }
                        else // delete project
                        {
                            itemsNode.RemoveChild(itemNode);
                        }
                    }

                    xmlDoc.Save(configFile);
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
        }

        public void UpdateCache()
        {
            StringBuilder cache = new StringBuilder();

            foreach (AGMLiteEntity req in LiteEntityCollection.Where(o => !o.IsNew))
            {
                cache.AppendFormat("{0},", req.Entity.Id.Value.ToString());
            }

            if (!System.IO.Directory.Exists(System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "Cache")))
                System.IO.Directory.CreateDirectory(System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "Cache"));
            using (StreamWriter sw = new StreamWriter(_cacheFile, false))
            {
                sw.Write(cache.Length > 0 ? cache.ToString().Substring(0, cache.ToString().Length - 1) : string.Empty);
                sw.Flush();
                sw.Close();
            }
        }

        public void UpdateNewStatusAndCache()
        {
            if (LiteEntityCollection.Where(o => o.IsNew).Count() == 0)
            {
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    Status = 0;
                    LastStatus = 0;
                });
                SaveLatestStatus();
            }
            UpdateCache();
        }

        public void UpdateAllFields()
        {
            foreach (AGMLiteEntityFilterField field in FilterFieldCollection)
            {
                UpdateField(field, EntityType);
            }
            foreach (AGMLiteEntityViewField field in ViewFieldCollection)
            {
                UpdateField(field, EntityType);
            }
        }

        public void UpdateField(AGMLiteEntityField field, Common.AGMLiteEnum.EntityType entityType)
        {
            string fieldtype = Common.AGMLiteEnum.GetEntityTypeFromEnum(entityType);
            List<string> availableFieldTypes = new List<string>();
            availableFieldTypes.Add(fieldtype);
            if (entityType == Common.AGMLiteEnum.EntityType.Requirement || entityType == Common.AGMLiteEnum.EntityType.Defect)
                availableFieldTypes.Add("release-backlog-item");

            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
            {
                AGMField entityfield = Project.EntityFields[field.Field.Entity].Where(o => o.Name.Equals(field.Field.Name)).Single() as AGMField;
                field.Field.Active = entityfield.Active;
                field.Field.Connection = entityfield.Connection;
                field.Field.Editable = entityfield.Editable;
                field.Field.Entity = entityfield.Entity;
                field.Field.Filterable = entityfield.Filterable;
                field.Field.Groupable = entityfield.Filterable;
                field.Field.History = entityfield.History;
                field.Field.IsToUpdate = entityfield.IsToUpdate;
                field.Field.Label = entityfield.Label;
                field.Field.ListId = entityfield.ListId;
                field.Field.MemoContainsHtml = entityfield.MemoContainsHtml;
                field.Field.Options = entityfield.GetOptions();
                field.Field.ReferenceType = entityfield.ReferenceType;
                field.Field.Required = entityfield.Required;
                field.Field.Size = entityfield.Size;
                field.Field.SupportsMultivalue = entityfield.SupportsMultivalue;
                field.Field.System = entityfield.System;
                field.Field.Type = entityfield.Type;
                field.Field.Verify = entityfield.Verify;
                field.Field.VersionControlled = entityfield.VersionControlled;
                field.Field.Virtual = entityfield.Virtual;
                field.Field.Visible = entityfield.Visible;             
                field.AvailableFieldsPool = Project.EntityFields;
                field.AvailableFieldOptions = field.Field.Options;
                field.AvailableFields = Project.EntityFields[field.Field.Entity];
                field.AvailableFieldTypes = availableFieldTypes;
            });
        }
        #endregion


        #region Private methods

        private void InitialTimer()
        {
            ItemTimer = new System.Timers.Timer();
            ItemTimer.AutoReset = true;
            ItemTimer.Elapsed += new System.Timers.ElapsedEventHandler(ItemTimer_Elapsed);
        }

        private void ItemTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!IsEditing && Project != null)
            {
                Sync();
            }                
        }

        private List<AGMField> BuildQueryFilter()
        {
            List<AGMField> filters = new List<AGMField>();
            foreach (AGMLiteEntityFilterField ff in FilterFieldCollection)
            {
                string value = ff.Field.Value;

                if (string.IsNullOrEmpty(ff.Field.Value))
                    value = string.Format("\"\"");

                switch (ff.Operation)
                {
                    case Common.AGMLiteEnum.Operation.Equals:
                        value = string.IsNullOrEmpty(value) ? ff.Field.Value : value;
                        break;
                    case Common.AGMLiteEnum.Operation.Contain:
                        value = string.Format("'*{0}*'", ff.Field.Value);
                        break;
                    case Common.AGMLiteEnum.Operation.StartWith:
                        value = string.Format("'{0}*'", ff.Field.Value);
                        break;
                    case Common.AGMLiteEnum.Operation.LargerThan:
                        value = string.Format(">{0}", ff.Field.Value);
                        break;
                    case Common.AGMLiteEnum.Operation.LargerThanEquals:
                        value = string.Format(">={0}", ff.Field.Value);
                        break;
                    case Common.AGMLiteEnum.Operation.SmallerThan:
                        value = string.Format("<{0}", ff.Field.Value);
                        break;
                    case Common.AGMLiteEnum.Operation.SmallerThanEquals:
                        value = string.Format("<={0}", ff.Field.Value);
                        break;
                    case Common.AGMLiteEnum.Operation.NotEquals:
                        value = string.Format("Not '{0}'", ff.Field.Value);
                        break;
                    case Common.AGMLiteEnum.Operation.NotContain:
                        value = string.Format("Not '*{0}*'", ff.Field.Value);
                        break;
                }

                filters.Add(new AGMField { Name = ff.Field.Name, Value = value, Entity = ff.Field.Entity });
            }

            return filters;
        }

        private void SyncWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (Project.Connection != null)
            {
                bool isfail = false;
                string msg = null;
                int retry = 0;

                if (!IsSyncImmediatelly)
                {
                    Random rnd = new Random((int)DateTime.Now.Ticks);
                    System.Threading.Thread.Sleep(rnd.Next(1, 60) * 1000);
                }

                #region Set Message and Status
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    Message = "Syncing now...";
                    Status = 3;
                    Total = 0;
                });
                #endregion

                #region Try 3 times if any issue happen
                while (retry < 3)
                {                    
                    try
                    {
                        switch (EntityType)
                        {
                            case Common.AGMLiteEnum.EntityType.Requirement:
                                SyncRequirement();
                                break;
                            case Common.AGMLiteEnum.EntityType.Defect:
                                SyncDefect();
                                break;
                            case Common.AGMLiteEnum.EntityType.ProjectTask:
                                SyncTask();
                                break;
                        }                       
                        isfail = false;
                        break;
                    }
                    catch (Exception ex)
                    {
                        isfail = true;
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            msg = ex.Message;
                        });
                    }

                    retry++;
                    System.Threading.Thread.Sleep(1000);
                }
                #endregion

                #region Set Message and Status when fail
                if (isfail)
                {
                    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                    {
                        Status = 1; // warning
                        Message = msg;
                    });
                }
                #endregion
            }
        }

        private void SyncWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (_syncWorker != null)
            {
                _syncWorker.Dispose();
                _syncWorker = null;
            }            

            if (Project.Connection != null)
            {
                SaveLatestStatus();
                IsSyncImmediatelly = false;
                // if not fail or not new item, set to normal.
                if (Status == 3)
                {
                    if (Application.Current != null)
                    {
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            Status = 0;
                        });
                    }
                }                    
            } 
        }
        
        private List<string> LoadCacheItems(string cachefile)
        {
            string cachestr = null;
            List<string> caches = new List<string>();
            using (StreamReader sr = new StreamReader(cachefile))
            {
                cachestr = sr.ReadToEnd();
            }

            if (!string.IsNullOrEmpty(cachestr))
            {
                caches = cachestr.Split(new char[] { ',' }).ToList();
            }

            return caches;
        }

        void liteEntity_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("IsNew"))
            {
                if (((AGMLiteEntity)sender).IsNew == false)
                {
                    UpdateNewStatusAndCache();
                }
            }
        }

        private Dictionary<int, AGMField> BuildViewFields()
        {
            Dictionary<int, AGMField> fields = new Dictionary<int, AGMField>();
            foreach (AGMLiteEntityViewField viewfield in ViewFieldCollection.OrderBy(o=>o.Order))
            {
                fields.Add(viewfield.Order, new AGMField { Name = viewfield.Field.Name, Entity = viewfield.Field.Entity });
            }

            return fields;
        }

        private void SyncRequirement()
        {
            HPAGMRestAPIWrapper.AGMRequirements items = new HPAGMRestAPIWrapper.AGMRequirements(Project.Connection);
            items.SortFields = new List<AGMField>();
            items.SortFields.Add(new AGMField { Name = "id", Entity = "requirement", Value="DESC" });
            List<AGMField> filters = BuildQueryFilter();
            Dictionary<int, AGMField> viewFields = BuildViewFields();

            //TODO: have to hard code here, only select out user stories;
            filters.Add(new AGMField { Name = "type-id", Value = "70" });

            List<HPAGMRestAPIWrapper.AGMRequirement> entityList = items.Search(filters, null);
            
            if (entityList != null)
            {
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    Total = entityList.Count;
                });

                // when cache file exists
                if (System.IO.File.Exists(_cacheFile))
                {
                    List<string> cacheItems = LoadCacheItems(_cacheFile);
                    // Build Requirment Entity Item Collection
                    foreach (HPAGMRestAPIWrapper.AGMRequirement entity in entityList)
                    {
                        // When the entity is existed in Cache.
                        if (cacheItems.Contains(entity.Id.Value.ToString()))
                        {
                            // When the entity is not existed in collection, add it to collection, but isNew is false
                            if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)
                            {
                                AGMLiteEntity liteEntity = new AGMLiteEntity
                                {
                                    Entity = entity,
                                    IsNew = false,
                                    Health = 0,
                                    HealthMessage = string.Empty,
                                    TaskBarIcon = TaskBarIcon,
                                    EntityType = Common.AGMLiteEnum.EntityType.Requirement,
                                    BallonStaySeconds = 3,
                                    Project = Project,
                                    ViewFields = viewFields,
                                };
                                liteEntity.PropertyChanged += new PropertyChangedEventHandler(liteEntity_PropertyChanged);

                                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                                {                                    
                                    LiteEntityCollection.Add(liteEntity);
                                });
                            }
                        }
                        else // when the entity is not in cache, add it, but IsNew is true
                        {
                            if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)
                            {
                                AGMLiteEntity liteEntity = new AGMLiteEntity
                                {
                                    Entity = entity,
                                    IsNew = true,
                                    Health = 0,
                                    HealthMessage = string.Empty,
                                    TaskBarIcon = TaskBarIcon,
                                    EntityType = Common.AGMLiteEnum.EntityType.Requirement,
                                    BallonStaySeconds = 3,
                                    Project = Project,
                                    ViewFields = viewFields,
                                };
                                liteEntity.PropertyChanged += new PropertyChangedEventHandler(liteEntity_PropertyChanged);

                                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                                {                                    
                                    LiteEntityCollection.Add(liteEntity);
                                });
                            }
                        }
                    }

                    #region Check if new item coming
                    int newItemCount = LiteEntityCollection.Where(o => o.IsNew).Count();
                    if (newItemCount > 0)
                    {
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            Message = "New item";
                            Status = 2; // New Item
                        });
                        if (newItemCount == 1)
                        {
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                (LiteEntityCollection.Where(o => o.IsNew).Single() as AGMLiteEntity).ShowNewItemMessage(Name);
                            });
                        }
                        else
                        {
                            StringBuilder sb = new StringBuilder();
                            int c = 0;
                            foreach (AGMLiteEntity liteEntity in LiteEntityCollection.Where(o => o.IsNew))
                            {
                                if (c > 2)
                                    break;
                                sb.AppendLine(string.Format("{0} - {1}...", 
                                    liteEntity.Entity.GetField("id").Value, 
                                    liteEntity.Entity.GetField("name").Value.Length > 30 ? liteEntity.Entity.GetField("name").Value.Substring(0, 30) : liteEntity.Entity.GetField("name").Value));
                                c++;
                            }
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                ShowNewItemsMessage(Name, sb.ToString(), newItemCount, 5);
                            });
                        }
                    }
                    else
                    {
                        //UpdateCache();
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            Status = 0;
                        });
                    }
                    #endregion
                }
                else //  when cache is not existed
                {
                    // Build Requirment Entity Item Collection
                    foreach (HPAGMRestAPIWrapper.AGMRequirement entity in entityList)
                    {
                        if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)
                        {
                            AGMLiteEntity liteEntity = new AGMLiteEntity
                            {
                                Entity = entity,
                                IsNew = false,
                                Health = 0,
                                HealthMessage = string.Empty,
                                TaskBarIcon = TaskBarIcon,
                                EntityType = Common.AGMLiteEnum.EntityType.Requirement,
                                BallonStaySeconds = 3,
                                Project = Project,
                                ViewFields = viewFields,
                            };
                            liteEntity.PropertyChanged += new PropertyChangedEventHandler(liteEntity_PropertyChanged);

                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {                                
                                LiteEntityCollection.Add(liteEntity);
                            });
                        }
                    }
                   
                    UpdateCache();
                    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                    {
                        Message = string.Empty;
                        Status = 0; // Normal
                    });
                }
            }
        }

        private void SyncDefect()
        {
            HPAGMRestAPIWrapper.AGMDefects items = new HPAGMRestAPIWrapper.AGMDefects(Project.Connection);
            List<AGMField> filters = BuildQueryFilter();
            Dictionary<int, AGMField> viewFields = BuildViewFields();
            items.SortFields = new List<AGMField>();
            items.SortFields.Add(new AGMField { Name = "id", Entity = "defect", Value = "DESC" });

            List<HPAGMRestAPIWrapper.AGMDefect> entityList = items.Search(filters, null);
            if (entityList != null)
            {
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    Total = entityList.Count;
                });

                if (System.IO.File.Exists(_cacheFile))
                {
                    List<string> cacheItems = LoadCacheItems(_cacheFile);                    
                    // Build Defect Entity Item Collection
                    foreach (HPAGMRestAPIWrapper.AGMDefect entity in entityList)
                    {
                        if (cacheItems.Contains(entity.Id.Value.ToString())) // the entity is in cache
                        {
                            if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0) // but not loaded into LiteEntityCollection, add it
                            {
                                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                                {
                                    LiteEntityCollection.Add(new AGMLiteEntity
                                    {
                                        Entity = entity,
                                        IsNew = false,
                                        Health = 0,
                                        HealthMessage = string.Empty,
                                        TaskBarIcon = TaskBarIcon,
                                        EntityType = Common.AGMLiteEnum.EntityType.Defect,
                                        BallonStaySeconds = 3,
                                        Project = Project,
                                        ViewFields = viewFields,
                                    });
                                });
                            }
                        }
                        else // the entity is not in cache, it is new
                        {
                            if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)  // and not loaded into LiteEntityCollection, add it
                            {
                                
                                AGMLiteEntity liteEntity = new AGMLiteEntity
                                {
                                    Entity = entity,
                                    IsNew = true,
                                    Health = 0,
                                    HealthMessage = string.Empty,
                                    TaskBarIcon = TaskBarIcon,
                                    EntityType = Common.AGMLiteEnum.EntityType.Defect,
                                    BallonStaySeconds = 3,
                                    Project = Project,
                                    ViewFields = viewFields,
                                };

                                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                                {
                                    //liteEntity.ShowNewItemMessage(Name);
                                    LiteEntityCollection.Add(liteEntity);        
                                });
                            }
                        }
                    }

                    #region Check if new item coming
                    int newItemCount = LiteEntityCollection.Where(o => o.IsNew).Count();
                    if (newItemCount > 0)
                    {
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            Message = "New item";
                            Status = 2; // New Item
                        });
                        if (newItemCount == 1)
                        {
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                (LiteEntityCollection.Where(o => o.IsNew).Single() as AGMLiteEntity).ShowNewItemMessage(Name);
                            });
                        }
                        else
                        {
                            StringBuilder sb = new StringBuilder();
                            int c = 0;
                            foreach (AGMLiteEntity liteEntity in LiteEntityCollection.Where(o => o.IsNew))
                            {
                                if (c > 2)
                                    break;
                                sb.AppendLine(string.Format("{0} - {1}...",
                                    liteEntity.Entity.GetField("id").Value,
                                    liteEntity.Entity.GetField("name").Value.Length > 30 ? liteEntity.Entity.GetField("name").Value.Substring(0, 30) : liteEntity.Entity.GetField("name").Value));
                                c++;
                            }
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                ShowNewItemsMessage(Name, sb.ToString(), newItemCount, 5);
                            });
                        }
                    }
                    else
                    {
                        //UpdateCache();
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            Status = 0;
                        });
                    }
                    #endregion
                }
                else //  cache file is not exist, add all entity to cache.
                {
                    // Build Defect Entity Item Collection
                    foreach (HPAGMRestAPIWrapper.AGMDefect entity in entityList)
                    {
                        if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)
                        {
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                LiteEntityCollection.Add(new AGMLiteEntity
                                {
                                    Entity = entity,
                                    IsNew = false,
                                    Health = 0,
                                    HealthMessage = string.Empty,
                                    TaskBarIcon = TaskBarIcon,
                                    EntityType = Common.AGMLiteEnum.EntityType.Defect,
                                    BallonStaySeconds = 3,
                                    Project = Project,
                                    ViewFields = viewFields,
                                });
                            });
                        }
                    }

                    UpdateCache();
                    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                    {
                        Message = string.Empty;
                        Status = 0; // Normal
                    });
                }
            }
        }

        private void SyncTask()
        {
            HPAGMRestAPIWrapper.AGMProjectTasks items = new HPAGMRestAPIWrapper.AGMProjectTasks(Project.Connection);
            Dictionary<int, AGMField> viewFields = BuildViewFields();
            items.SortFields = new List<AGMField>();
            items.SortFields.Add(new AGMField { Name = "id", Entity = "project-task", Value = "DESC" });

            List<HPAGMRestAPIWrapper.AGMProjectTask> entityList = items.Search(BuildQueryFilter(), null);
            if (entityList != null)
            {
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    Total = entityList.Count;
                });
                
                if (System.IO.File.Exists(_cacheFile))
                {
                    List<string> cacheItems = LoadCacheItems(_cacheFile);

                    // Build Task Entity Item Collection
                    foreach (HPAGMRestAPIWrapper.AGMProjectTask entity in entityList)
                    {
                        if (cacheItems.Contains(entity.Id.Value.ToString()))
                        {
                            if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)
                            {
                                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                                {
                                    LiteEntityCollection.Add(new AGMLiteEntity
                                    {
                                        Entity = entity,
                                        IsNew = false,
                                        Health = 0,
                                        HealthMessage = string.Empty,
                                        TaskBarIcon = TaskBarIcon,
                                        EntityType = Common.AGMLiteEnum.EntityType.ProjectTask,
                                        BallonStaySeconds = 3,
                                        Project = Project,
                                        ViewFields = viewFields,
                                    });
                                });
                            }
                        }
                        else
                        {
                            if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)
                            {
                                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                                {
                                    AGMLiteEntity liteEntity = new AGMLiteEntity
                                    {
                                        Entity = entity,
                                        IsNew = true,
                                        Health = 0,
                                        HealthMessage = string.Empty,
                                        TaskBarIcon = TaskBarIcon,
                                        EntityType = Common.AGMLiteEnum.EntityType.ProjectTask,
                                        BallonStaySeconds = 3,
                                        Project = Project,
                                        ViewFields = viewFields,
                                    };
                                    LiteEntityCollection.Add(liteEntity);
                                    liteEntity.ShowNewItemMessage(Name);
                                    System.Threading.Thread.Sleep(4000);
                                });                                
                            }
                        }
                    }

                    #region Check if new item coming
                    int newItemCount = LiteEntityCollection.Where(o => o.IsNew).Count();
                    if (newItemCount > 0)
                    {
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            Message = "New item";
                            Status = 2; // New Item
                        });
                        if (newItemCount == 1)
                        {
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                (LiteEntityCollection.Where(o => o.IsNew).Single() as AGMLiteEntity).ShowNewItemMessage(Name);
                            });
                        }
                        else
                        {
                            StringBuilder sb = new StringBuilder();
                            int c = 0;
                            foreach (AGMLiteEntity liteEntity in LiteEntityCollection.Where(o => o.IsNew))
                            {
                                if (c > 2)
                                    break;
                                sb.AppendLine(string.Format("{0} - {1}...", 
                                    liteEntity.Entity.GetField("id").Value, 
                                    liteEntity.Entity.GetField("description").Value.Length > 30 ? liteEntity.Entity.GetField("description").Value.Substring(0, 30) : liteEntity.Entity.GetField("description").Value));
                                c++;
                            }
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                ShowNewItemsMessage(Name, sb.ToString(), newItemCount, 5);
                            });
                        }
                    }
                    else
                    {
                        //UpdateCache();
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                        {
                            Status = 0;
                        });
                    }
                    #endregion
                }
                else
                {
                    // Build TAsk Entity Item Collection
                    foreach (HPAGMRestAPIWrapper.AGMProjectTask entity in entityList)
                    {
                        if (LiteEntityCollection.Where(o => o.Entity.Id.Value == entity.Id.Value).Count() == 0)
                        {
                            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                            {
                                LiteEntityCollection.Add(new AGMLiteEntity
                                {
                                    Entity = entity,
                                    IsNew = false,
                                    Health = 0,
                                    HealthMessage = string.Empty,
                                    TaskBarIcon = TaskBarIcon,
                                    EntityType = Common.AGMLiteEnum.EntityType.ProjectTask,
                                    BallonStaySeconds = 3,
                                    Project = Project,
                                    ViewFields = viewFields,
                                });
                            });
                        }
                    }

                    UpdateCache();
                    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                    {
                        Message = string.Empty;
                        Status = 0; // Normal
                    });
                }
            }
        }

        private void ShowNewItemsMessage(string itemName, string text, int newitemCount, int BallonStaySeconds)
        {
            string iconpath = null;
            string subtitle = null;

            try
            {
                switch (EntityType)
                {
                    case Common.AGMLiteEnum.EntityType.Requirement:
                        iconpath = @"/images/req.png";
                        break;
                    case Common.AGMLiteEnum.EntityType.Defect:
                        iconpath = @"/images/bug.png";
                        break;
                    case Common.AGMLiteEnum.EntityType.ProjectTask:
                        iconpath = @"/images/task.png";
                        break;
                }
                subtitle = string.Format("There are {0} new {1}.", newitemCount, EntityType.ToString());
                int sec = BallonStaySeconds == 0 ? 5 : BallonStaySeconds * 1000;
                FancyBalloon balloon = new FancyBalloon();
                balloon.BalloonTitle = itemName;
                balloon.BalloonText = text.Length > 100 ? string.Format("{0}...", text.Substring(0, 100)) : string.Format("{0}", text);
                balloon.BalloonIcon = iconpath;
                balloon.BalloonSubTitle = subtitle;
                balloon.DoubleClickEvent += new RoutedEventHandler(balloon_DoubleClickEvent);
                //show balloon and close it after 5 seconds
                TaskBarIcon.ShowCustomBalloon(balloon, PopupAnimation.Slide, sec);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        private void balloon_DoubleClickEvent(object sender, RoutedEventArgs e)
        {
            EntityListViewModel entitiesVM = new EntityListViewModel(this);
            EntityListWindow entitylistWindow = new EntityListWindow();
            entitylistWindow.DataContext = entitiesVM;
            entitylistWindow.WindowStartupLocation = WindowStartupLocation.Manual;
            if (entitiesVM.CloseAction == null) entitiesVM.CloseAction = new Action(() => entitylistWindow.Close());
            entitylistWindow.ShowDialog();
        }

        #region INotifyPropertyChanged Members
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
        #endregion
    }
}
